package org.jiuling.gaode.geocode;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONException;
import org.jiuling.basics.converter.StringConvert;
import org.jiuling.basics.validation.IntegerVerify;
import org.jiuling.basics.validation.ObjectVerify;
import org.jiuling.basics.validation.StringVerify;
import org.jiuling.gaode.config.GaoDeConfig;
import org.jiuling.gaode.enums.ResponseFormatEnum;
import org.jiuling.gaode.geocode.dto.GeoCodeParamDto;
import org.jiuling.gaode.geocode.dto.ReGeoCodeParamDto;
import org.jiuling.gaode.geocode.enums.ExtensionsEnum;
import org.jiuling.gaode.geocode.exception.GaoDeLocationException;
import org.jiuling.gaode.geocode.model.GeoCodeResponseModel;
import org.jiuling.gaode.geocode.model.ReGeoCodeResponseModel;
import org.jiuling.request.http.HttpRequest;
import org.jiuling.request.http.factory.HttpRequestFactory;
import org.jiuling.request.http.model.HttpRequestParam;

import java.io.IOException;

/**
 * 高德地理编码
 * @author 紫灵
 * @date 2024年11月14日 17时04分33秒
 */
public class GaoDeGeocode {

    private final GaoDeConfig gaoDeConfig;
    /** 地理编码URL */
    private final String GEOGRAPHIC_CODE_URL = "https://restapi.amap.com/v3/geocode/geo";
    /** 逆地理编码URL */
    private final String REVERSE_GEOGRAPHIC_CODE_URL = "https://restapi.amap.com/v3/geocode/regeo";

    public GaoDeGeocode(GaoDeConfig gaoDeConfig) {
        this.gaoDeConfig = gaoDeConfig;
    }

    /**
     * 获取地理编码
     * @param address 地址
     * @return 地理编码响应模型
     * @throws GaoDeLocationException 高德定位异常
     */
    public GeoCodeResponseModel getGeographicCode(String address) throws GaoDeLocationException {
        return getGeographicCode(
                GeoCodeParamDto.builder()
                        .addAddress(address)
                        .build()
        );

    }

    /**
     * 获取地理编码
     * @param geoCodeParamDto 地理编码参数数据传输对象
     * @return 地理编码响应模型
     * @throws GaoDeLocationException 高德定位异常
     */
    public GeoCodeResponseModel getGeographicCode(GeoCodeParamDto geoCodeParamDto) throws GaoDeLocationException {
        HttpRequest httpRequest = HttpRequestFactory.openHttpRequest(GEOGRAPHIC_CODE_URL);
        // 封装的公共请求参数
        HttpRequestParam httpRequestParam = packagePublicHttpRequestParam(
                // 数字签名
                geoCodeParamDto.getSig(),
                // 响应格式
                geoCodeParamDto.getOutput(),
                // 回调函数
                geoCodeParamDto.getCallback()
        )
                // 地址
                .add("address", geoCodeParamDto.getAddress());
        String city = geoCodeParamDto.getCity();
        if (!StringVerify.isBlank(city)){
            httpRequestParam.add("city", city);
        }
        httpRequest.setHttpRequestParam(httpRequestParam);
        try {

            GeoCodeResponseModel geoCodeResponseModel = JSON.parseObject(httpRequest.send().getBody(), GeoCodeResponseModel.class);
            if (!geoCodeResponseModel.isStatus()){
                if (geoCodeResponseModel.getInfoCode() == 10021){
                    return this.getGeographicCode(geoCodeParamDto);
                }
            }
            return geoCodeResponseModel;
        } catch (IOException | JSONException e) {
            throw new GaoDeLocationException(e);
        }
    }

    /**
     * 获取逆向地理编码
     * @param location 坐标
     * @return 逆向地理编码响应模型
     * @throws GaoDeLocationException 高德定位异常
     */
    public ReGeoCodeResponseModel getReGeographicCode(String location) throws GaoDeLocationException {
        return getReGeographicCode(
                ReGeoCodeParamDto.builder()
                        .addLocation(location)
                        .build()
        );

    }

    /**
     * 获取逆向地理编码
     * @param reGeoCodeParamDto 逆向编码参数数据传输对象
     * @return 逆向地理编码响应模型
     */
    public ReGeoCodeResponseModel getReGeographicCode(ReGeoCodeParamDto reGeoCodeParamDto) throws GaoDeLocationException {
        HttpRequest httpRequest = HttpRequestFactory.openHttpRequest(REVERSE_GEOGRAPHIC_CODE_URL);
        // 封装的公共请求参数
        HttpRequestParam httpRequestParam = packagePublicHttpRequestParam(
                // 数字签名
                reGeoCodeParamDto.getSig(),
                // 响应格式
                reGeoCodeParamDto.getOutput(),
                // 回调函数
                reGeoCodeParamDto.getCallback()
        )
                // 坐标
                .add("location", reGeoCodeParamDto.getLocation());
        String poitype = reGeoCodeParamDto.getPoitype();
        if (!StringVerify.isBlank(poitype)){
            httpRequestParam.add("poitype", poitype);
        }

        Integer radius = reGeoCodeParamDto.getRadius();
        if (!IntegerVerify.isBlank(radius)){
            httpRequestParam.add("radius", radius);
        }
        ExtensionsEnum extensions = reGeoCodeParamDto.getExtensions();
        if (!ObjectVerify.isNull(extensions)){
            httpRequestParam.add("extensions", StringConvert.toLowerCase(extensions.name()));
        }
        Integer roadlevel = reGeoCodeParamDto.getRoadlevel();
        if (!IntegerVerify.isBlank(roadlevel)){
            httpRequestParam.add("roadlevel", roadlevel);
        }
        Integer homeorcorp = reGeoCodeParamDto.getHomeorcorp();
        if (!IntegerVerify.isBlank(homeorcorp)){
            httpRequestParam.add("homeorcorp", homeorcorp);
        }
        httpRequest.setHttpRequestParam(httpRequestParam);
        try {
            String body = httpRequest.send().getBody();
            ReGeoCodeResponseModel reGeoCodeResponseModel = JSON.parseObject(body, ReGeoCodeResponseModel.class);
            if (!reGeoCodeResponseModel.isStatus()){
                if (reGeoCodeResponseModel.getInfoCode() == 10021){
                    return this.getReGeographicCode(reGeoCodeParamDto);
                }
            }
            return reGeoCodeResponseModel;

        } catch (IOException | JSONException e) {
            throw new GaoDeLocationException(e);
        }
    }

    /**
     * 封装公共请求参数
     * @param sig 数字签名
     * @param output 响应格式
     * @param callback 回调函数
     * @return http请求参数
     */
    private HttpRequestParam packagePublicHttpRequestParam(String sig,ResponseFormatEnum output,String callback){

        HttpRequestParam httpRequestParam =  new HttpRequestParam()
                .add("key", this.gaoDeConfig.getKey());

        // 数字签名
        if (!StringVerify.isBlank(sig)){
            httpRequestParam.add("sig", sig);
        }

        // 数字签名
        if (!ObjectVerify.isNull(output)){
            httpRequestParam.add("output", output.name());
        }

        // 回调函数
        if (!StringVerify.isBlank(callback)){
            httpRequestParam.add("callback", callback);
        }

        return httpRequestParam;
    }

}
